package com.yichat.micro.msghttpservice.core.retry;

import com.yichat.micro.msghttpservice.common.Constants;
import com.yichat.micro.msghttpservice.common.util.JsonUtil;
import com.yichat.micro.msghttpservice.common.vo.MsgHttpRequestVo;
import com.yichat.micro.msghttpservice.core.base.AbstractHttpCaller;
import com.yichat.micro.msghttpservice.core.record.service.RecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Created by chenmohan on 2019/04/15
 *
 * @description
 */
@Component
@Slf4j
public class RetryManager {

    @Value("${msg-http-service.retry.delay-queue.fetch.num}")
    private int retryDelayQueueFetchNumber;

    @Value("${msg-http-service.protocol-http.impl}")
    String httpImpl;

    private static DelayQueue<RetryMsgHttpRequestVo> delayQueue = new DelayQueue<>();

    private static ScheduledExecutorService scheduledExecutorService;

    private AbstractHttpCaller caller;

    @Autowired
    private RecordService recordService;

    @Autowired
    ApplicationContext applicationContext;

    /**
     * 初始化caller,recoredService,和1个定时调度线程
     */
    @PostConstruct
    public void init() {
        //注入Bean
        caller = applicationContext.getBean(httpImpl, AbstractHttpCaller.class);
        //     Boolean asyncRecord = applicationContext.getEnvironment().getProperty("msg-http-service.async-record.enable", Boolean.class);
        //      this.recordService = this.applicationContext.getBean(asyncRecord ? "asyncRecordServiceImpl" : "recordServiceImpl", RecordService.class);
        //定时调度线程每1秒去扫描队列中的超时请求
        scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            ifdelayTimeoutAndProcess();
        }, 0, 1000, TimeUnit.MILLISECONDS);
        log.info("【STARTING】::初始化延时队列扫描线程完成");
    }


    /**
     * 将前端请求参数对象{@link MsgHttpRequestVo}包装成一个可重试请求的业务对象{@link RetryMsgHttpRequestVo}
     *
     * @param vo
     * @param sid
     */
    public RetryMsgHttpRequestVo wrap(MsgHttpRequestVo vo, long sid) {
        //检查当前重试是否达到上限
        RetryMsgHttpRequestVo retryVo = RetryMsgHttpRequestVo.buildRetryVo(vo, sid);

        //记录日志
        recordService.recordBeforeResponse(retryVo);

        return retryVo;
    }


    /**
     * 把重试的请求对象放到延时队列中
     *
     * @param retryVo 重试请求对象
     */
    public void delayAndRetry(RetryMsgHttpRequestVo retryVo) {
        //检查当前重试是否达到上限
        retryVo = RetryMsgHttpRequestVo.buildRetryVo(retryVo);
        if (retryVo.getCurrentRetryTimes() <= retryVo.getVo().getReTry()) {
            // System.err.println(retryVo.getList().get(retryVo.getCurrentListIndex()).getUrl() + "第" + retryVo.getCurrentRetryTimes() + "次重试");
            delayQueue.offer(retryVo);
        } else if (retryVo.isBatch()) {
            //记录当前批量子请求失败
            recordService.recordWithRetryFailed(retryVo);
            System.out.println("批量请求中的单条失败, buz = " + retryVo.getVo().getBuz() + " , currentTryTimes = " + retryVo.getCurrentRetryTimes() + " ,recordId = " + retryVo.getSid() + "-" + retryVo.getCurrentListIndex());
            //如果是批量请求,且单条请求失败后会继续请求直达全部请求完成
            if (Constants.enalbe == retryVo.getVo().getFailedContinue()
                    && retryVo.next()) {
                //System.out.println("下一个请求的部分信息: " + retryVo.getList().get(retryVo.getCurrentListIndex()).getUrl());
                applicationContext.publishEvent(new BatchRequestEvent(this, retryVo));
            } else {
                //记录整个批量请求最终失败
                recordService.recoredWithBatchFailed(retryVo);
            }
        } else {
            //非批量请求,记录本条请求失败
            System.out.println("非批量请求失败, info=>" + JsonUtil.toJSON(retryVo));
            recordService.recordWithRetryFailed(retryVo);
        }


    }

    /**
     * 将超时的请求从队列中取出并重试调用请求
     */
    public void ifdelayTimeoutAndProcess() {
        List<RetryMsgHttpRequestVo> expires = new ArrayList<>(retryDelayQueueFetchNumber);
        delayQueue.drainTo(expires, retryDelayQueueFetchNumber);
        if (expires != null) {
            expires.parallelStream().forEach(e -> callerProcess(e));
        }
    }

    private void callerProcess(RetryMsgHttpRequestVo retryVo) {
        caller.process(retryVo);
    }

    public static DelayQueue<RetryMsgHttpRequestVo> getDelayQueue() {
        return delayQueue;
    }

}
